Flutterで軽量なRDBMSのSQLite(sqflite)を使ってみた
こんにちは、ゲームソリューション部のsoraです。
今回は、Flutterで軽量なRDBMSのSQLite(sqflite)を使ってみたことについて書いていきます。
実装した画面
ローカルのデータベースに対する各操作を実行するシンプルなアプリです。
Insert
id・name・messageを入力して、データを挿入します。
Update
idをキーとして、name・messageを更新します。
Delete
idをキーとして、データを削除します。
利用する主要なパッケージ
sqflite
sqflite | Flutter package
使用したバージョン(pubspec.yamlから抜粋):sqflite: ^2.3.2
軽量なリレーショナルデータベースマネジメントシステムであるSQLiteを使うためのパッケージ
flutter_riverpod
flutter_riverpod | Flutter package
使用したバージョン(pubspec.yamlから抜粋):flutter_riverpod: ^2.5.1
状態管理パッケージ
多くの情報が落ちている人気なパッケージのため、詳しい説明は割愛します。
コードの解説
コードは以下です。
main.dart
ではメインの処理、database_connection
ではDB操作、chat_message.dart
ではテーブル内のデータ表示の状態管理をしています。
import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'chat_message.dart'; import 'database_connection.dart'; Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); runApp( const ProviderScope( child: MyApp() ), ); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'sqflite Test', theme: ThemeData(), home: const ChatPage(), ); } } class ChatPage extends ConsumerWidget { const ChatPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { // DB内のデータの表示用 final chatMessageProviderValue = ref.watch(chatMessageProvider); // DB操作用 final dataBaseConnectionProviderNotifier = ref.watch(dataBaseConnectionProvider.notifier); final _idEditController = TextEditingController(); final _nameEditController = TextEditingController(); final _messageEditController = TextEditingController(); // DBの項目、idが主キー String id; String name; String message; return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: const Text('sqflite test'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ // 入力フィールド // id Container( padding: const EdgeInsets.only( left: 25, right: 25, ), child: TextField( keyboardType: TextInputType.text, controller: _idEditController, decoration: const InputDecoration( labelText: 'id', ) ) ), const SizedBox(height: 10), // name Container( padding: const EdgeInsets.only( left: 25, right: 25, ), child: TextField( keyboardType: TextInputType.text, controller: _nameEditController, decoration: const InputDecoration( labelText: 'name', ) ) ), const SizedBox(height: 10), // message Container( padding: const EdgeInsets.only( left: 25, right: 25, ), child: TextField( keyboardType: TextInputType.text, controller: _messageEditController, decoration: const InputDecoration( labelText: 'message', ) ) ), const SizedBox(height: 10), // ボタン Row( mainAxisAlignment: MainAxisAlignment.center, children: [ ElevatedButton( onPressed: () async{ id = _idEditController.text; name = _nameEditController.text; message = _messageEditController.text; dataBaseConnectionProviderNotifier.dbInsert(id, name, message); }, child: const Text('insert'), ), const SizedBox(width: 10), ElevatedButton( onPressed: () async{ id = _idEditController.text; name = _nameEditController.text; message = _messageEditController.text; dataBaseConnectionProviderNotifier.dbUpdate(id, name, message); }, child: const Text('update'), ), const SizedBox(width: 10), ElevatedButton( onPressed: () async{ id = _idEditController.text; dataBaseConnectionProviderNotifier.dbDelete(id); }, child: const Text('delete') ), ], ), const SizedBox(height: 10), for(var map in chatMessageProviderValue) ...{ Text('id = ${map["id"]}, name = ${map["name"]}, message = ${map["message"]}'), } ], ), ), ); } }
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:sqflite/sqflite.dart'; import 'chat_message.dart'; class DataBaseConnection extends AsyncNotifier<List<Map>> { late String path; late Database database; List<Map> listMap = []; @override Future<List<Map>> build() async { // getDatabasesPath():デフォルトのデータベース保存用フォルダのパスを取得 var databasesPath = await getDatabasesPath(); // 取得したパスから本アプリ用にて生成するDB名を指定 path = '$databasesPath/test.db'; // データベースを開く(pathに存在しなければ新規作成) database = await openDatabase( path, version: 1, // DBがpathに存在しなかった場合にonCreateが呼び出される onCreate: (Database db, int version) async { await db.execute( 'CREATE TABLE Test (id TEXT PRIMARY KEY, name TEXT, message TEXT)' ); }); return []; } void dbInsert(String id, String name, String message) async { final chatMessage = ref.watch(chatMessageProvider.notifier); // INSERT await database.insert( 'Test', {'id': id, 'name': name, 'message': message}, ); // SELECT listMap = await database.rawQuery('SELECT * FROM Test'); // 状態更新 chatMessage.update(listMap); } void dbUpdate(String id, String name, String message) async { final chatMessage = ref.watch(chatMessageProvider.notifier); // idをキーとしてUPDATE await database.update( 'Test', {'name': name, 'message': message}, where: 'id = ?', whereArgs: [id] ); // SELECT listMap = await database.rawQuery('SELECT * FROM Test'); // 状態更新 chatMessage.update(listMap); } void dbDelete(String id) async { final chatMessage = ref.watch(chatMessageProvider.notifier); // idをキーとしてDELETE await database.delete( 'Test', where: 'id = ?', whereArgs: [id] ); // SELECT listMap = await database.rawQuery('SELECT * FROM Test'); // 状態更新 chatMessage.update(listMap); } } final dataBaseConnectionProvider = AsyncNotifierProvider<DataBaseConnection, List<Map>>(() { return DataBaseConnection(); });
DBの初期化
まずデフォルトのデータベース保存用のフォルダパスを取得して、本アプリに使用するDB名を指定します。
指定したパスにDBが存在しなければ、DBを作成します。
コメント記載の通り、DBがパスに存在しない場合に、onCreateが実行されます。
// getDatabasesPath():デフォルトのデータベース保存用フォルダのパスを取得 var databasesPath = await getDatabasesPath(); // 取得したパスから本アプリ用にて生成するDB名を指定 path = '$databasesPath/test.db'; // データベースを開く(pathに存在しなければ新規作成) database = await openDatabase( path, version: 1, // DBがpathに存在しなかった場合にonCreateが呼び出される onCreate: (Database db, int version) async { await db.execute( 'CREATE TABLE Test (id TEXT PRIMARY KEY, name TEXT, message TEXT)' ); });
DB操作
次にDB操作の部分では、INSERT・UPDATE・DELETEをそれぞれメソッドとして書いています。
database.insert()
・database.update()
・database.delete()
などが用意されています。
database.rawQuery()
でSQL文を書いて指定することが可能です。
DB操作を行った後に、テーブル内の全データ取得をして、状態を更新しています。
void dbInsert(String id, String name, String message) async { final chatMessage = ref.watch(chatMessageProvider.notifier); // INSERT await database.insert( 'Test', {'id': id, 'name': name, 'message': message}, ); // SELECT listMap = await database.rawQuery('SELECT * FROM Test'); // 状態更新 chatMessage.update(listMap); } void dbUpdate(String id, String name, String message) async { final chatMessage = ref.watch(chatMessageProvider.notifier); // idをキーとしてUPDATE await database.update( 'Test', {'name': name, 'message': message}, where: 'id = ?', whereArgs: [id] ); // SELECT listMap = await database.rawQuery('SELECT * FROM Test'); // 状態更新 chatMessage.update(listMap); } void dbDelete(String id) async { final chatMessage = ref.watch(chatMessageProvider.notifier); // idをキーとしてDELETE await database.delete( 'Test', where: 'id = ?', whereArgs: [id] ); // SELECT listMap = await database.rawQuery('SELECT * FROM Test'); // 状態更新 chatMessage.update(listMap); }
参考にした記事
【Flutter】ローカルデータベース(sqflite)
SQLiteでのデータ永続化
最後に
今回は、Flutterで軽量なRDBMSのSQLite(sqflite)を使ってみたことを記事にしました。
どなたかの参考になると幸いです。